﻿using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;
using RevitProject.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace RevitProject.SpaceManager.Core
{
    internal class CustomRouteCore
    {
        internal static Document Doc  =  null;
        /// <summary>
        /// 创建路径线
        /// </summary>
        internal static void CreateRouteLine(List<Level> levels, Level curLevel)
        {
            Document doc = Doc;

            int curLevelIndex = levels.ToElementIds().IndexOf(curLevel.Id);

            double topLevelElv = curLevel.ProjectElevation + 4000d.MMToFeet();
            if (curLevelIndex != levels.Count - 1)
                topLevelElv = levels[curLevelIndex + 1].ProjectElevation;

            var pipeTypes = doc.OfClass<PipeType>().ToList();
            var pipeSystemTypes = doc.OfClass<PipingSystemType>().ToList();
            //当前标高空间
            var levelSpaces = doc.OfClass<SpatialElement>(BuiltInCategory.OST_MEPSpaces).Cast<Space>().ToList()
                .FindAll(p => p.LevelId.Equals(curLevel.Id));
            //当前标高路径线
            var levelRoutes = doc.OfClass<MEPCurve>(BuiltInCategory.OST_PlaceHolderPipes).Cast<Pipe>().ToList()
                .FindAll(p => p.ReferenceLevel.Id.Equals(curLevel.Id));
            //走廊路径线
            var corridorRoute = levelRoutes.FindAll(p => p.PipeType.Name.Contains("走廊"));
            if (corridorRoute==null || corridorRoute.Count == 0)
                return;

            var systemTypeId = corridorRoute.FirstOrDefault().GetMEPSystemType().Id;

            var corridorRouteLines = corridorRoute.Select(p => p.GetLocationCurve() as Line).ToList();
            //走廊标高
            double corridorElv = corridorRouteLines.FirstOrDefault().EndPoint0().Z;

            var wellRouteLines = levelRoutes.FindAll(p => p.PipeType.Name.Contains("管井"))
                .Select(p => p.GetLocationCurve() as Line).ToList();
            var roomRouteLines = levelRoutes.FindAll(p => p.PipeType.Name.Contains("房间"))
                .Select(p => p.GetLocationCurve() as Line).ToList();

            List<Room> linkRooms = GetLinkRoom();

            foreach (var space in levelSpaces)
            {
                string spaceRoomNum = space.get_Parameter(BuiltInParameter.SPACE_ASSOC_ROOM_NUMBER).AsString();
                if (spaceRoomNum.IsNullOrEmpty())
                    continue;
                var room = linkRooms.Find(p => p.GetParameterValueByName("编号") == spaceRoomNum);
                if (room == null)
                    continue;

                //房间中心点
                XYZ roomCenter = room.GetCentroid().Flatten(curLevel.ProjectElevation);

                var connectLines = corridorRouteLines.FindAll(p => p.IsOnLine(p.VerticalPointExt(roomCenter)));
                if (connectLines.IsNullOrEmptyExt())
                    continue;
                //房间与走廊的路径线
                var connectLine = connectLines.OrderBy(p => p.Distance(roomCenter)).FirstOrDefault();
                XYZ connectPt = connectLine.VerticalPointExt(roomCenter).Flatten(corridorElv);



                if (space.GetParameterValueByName("空间类别") == "管井")
                {
                    var wellSystemTypeId = systemTypeId;

                    PipeType wellPipeType = pipeTypes.Find(p => p.Name.Contains("管井"));
                    PipingSystemType wellSystemType = pipeSystemTypes.Find(p => p.Name.Contains("管井"));
                    if (wellSystemType != null)
                        wellSystemTypeId = wellSystemType.Id;

                    //管井中心线
                    Pipe pipe = null;
                    Line cWellLine = Line.CreateBound(roomCenter.Flatten(curLevel.ProjectElevation),
                        roomCenter.Flatten(topLevelElv));
                    if (!wellRouteLines.Exists(p => p.IsAlmostEqualTo(cWellLine)))
                    {
                        pipe = Pipe.CreatePlaceholder(doc, wellSystemTypeId, wellPipeType.Id, curLevel.Id,
                            cWellLine.EndPoint0(), cWellLine.EndPoint1());
                        pipe.SetParameterByName("标记", room.Name + "|" + spaceRoomNum);
                    }
                    //管井路径线
                    Line rWellLine = Line.CreateBound(roomCenter.Flatten(corridorElv), connectPt);
                    if (!wellRouteLines.Exists(p => p.IsAlmostEqualTo(rWellLine)))
                    {
                        pipe = Pipe.CreatePlaceholder(doc, wellSystemTypeId, wellPipeType.Id, curLevel.Id,
                            rWellLine.EndPoint0(), rWellLine.EndPoint1());
                        pipe.SetParameterByName("标记", room.Name + "|" + spaceRoomNum);
                    }
                }
                if (space.GetParameterValueByName("空间类别") == "房间")
                {
                    double roomHeight = room.GetParameterValueByName("房间设计净高").ToDouble();
                    if (roomHeight.IsLessEqExt(0))
                        continue;

                    var roomSystemTypeId = systemTypeId;

                    PipeType roomPipeType = pipeTypes.Find(p => p.Name.Contains("房间"));
                    PipingSystemType roomSystemType = pipeSystemTypes.Find(p => p.Name.Contains("房间"));
                    if (roomSystemType != null)
                        roomSystemTypeId = roomSystemType.Id;

                    XYZ centerTopPt = roomCenter.Flatten(curLevel.ProjectElevation + roomHeight.MMToFeet());

                    //创建房间中心垂线
                    Line cRoomLine = Line.CreateBound(roomCenter, centerTopPt);
                    if (!roomRouteLines.Exists(p => p.IsAlmostEqualTo(cRoomLine)))
                    {
                        Pipe.CreatePlaceholder(doc, roomSystemTypeId, roomPipeType.Id, curLevel.Id,
                            roomCenter, centerTopPt);
                    }
                    //房间路径线
                    Line rRoomLine = Line.CreateBound(centerTopPt, connectPt.Flatten(centerTopPt.Z));
                    if (!roomRouteLines.Exists(p => p.IsAlmostEqualTo(rRoomLine)))
                    {
                        Pipe pipe = Pipe.CreatePlaceholder(doc, roomSystemTypeId, roomPipeType.Id, curLevel.Id,
                            centerTopPt, connectPt.Flatten(centerTopPt.Z));
                        pipe.SetParameterByName("标记", room.Name + "|" + spaceRoomNum);
                    }

                    if (!centerTopPt.Z.IsEquals(corridorElv))
                    {
                        //连接房间路径和走廊路径的垂线
                        Line vRoomLine = Line.CreateBound(connectPt.Flatten(centerTopPt.Z), connectPt.Flatten(corridorElv));
                        if (!roomRouteLines.Exists(p => p.IsAlmostEqualTo(vRoomLine)))
                        {
                            Pipe.CreatePlaceholder(doc, roomSystemTypeId, roomPipeType.Id, curLevel.Id,
                                connectPt.Flatten(centerTopPt.Z), connectPt.Flatten(corridorElv));
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 获取路由数据
        /// </summary>
        /// <returns></returns>
        internal static List<RouteNodeData> GetRouteData(string sRoomName = "", string eRoomName = "")
        {
            //建筑楼层标高
            var levels = Doc.OfClass<Level>().ToList()
                .FindAll(p => p.get_Parameter(BuiltInParameter.LEVEL_IS_BUILDING_STORY).AsInteger() == 1);

            //路由节点
            List<RouteNodeData> routeNodes = new List<RouteNodeData>();

            var routePipes = GetLinkRoutePipeByLevel(levels);
            //走廊路径线
            var corridorRoutes = routePipes.FindAll(p => p.PipeType.Name.Contains("走廊"));
            if (corridorRoutes == null || corridorRoutes.Count == 0)
                return routeNodes;
            //拆分走廊线
            var corridorLines = corridorRoutes.Select(p => p.GetLocationCurve() as Line).ToList().GetSplitLines();

            //每层走廊的标高
            var tupleCorridorElvs = new List<Tuple<Level, double>>();
            foreach (var level in levels)
            {
                var corridorRoute = corridorRoutes.Find(p => p.ReferenceLevel.ProjectElevation.IsEquals(level.ProjectElevation));
                if (corridorRoute != null)
                {
                    tupleCorridorElvs.Add(new Tuple<Level, double>
                        (level, corridorRoute.GetLocationCurve().EndPoint0().Z));
                }
            }

            //管井路径线
            var wellRoutes = routePipes.FindAll(p => p.PipeType.Name.Contains("管井") && p.GetParameterValueStringByName("标记").IsNotNullEmptyExt());
            //拆分管井线
            var wellLines = wellRoutes.Select(p => p.GetLocationCurve() as Line).ToList().GetSplitLines();

            //房间路径线
            var roomRoutes = routePipes.FindAll(p => p.PipeType.Name.Contains("房间"));
            if (sRoomName != "" && eRoomName != "")
            {
                roomRoutes = roomRoutes.FindAll(p => p.GetParameterValueByName("标记") == sRoomName || p.GetParameterValueByName("标记") == eRoomName);
            }
            //房间线
            var roomLines = new List<Curve>();
            var tupleRoomLines = new List<Tuple<string, Curve>>();
            foreach (var roomRoute in roomRoutes)
            {
                var tuple = tupleCorridorElvs.Find(p => p.Item1.ProjectElevation.IsEquals(roomRoute.ReferenceLevel.ProjectElevation));
                if (tuple != null)
                {
                    string roomName = roomRoute.GetParameterValueByName("标记");
                    //房间线对齐走廊标高
                    var roomLine = roomRoute.GetLocationCurve().Flatten(tuple.Item2);
                    roomLines.Add(roomLine);
                    tupleRoomLines.Add(new Tuple<string, Curve>(roomName, roomLine));
                }
            }

            //房间和管井路径顶点
            var spaceVerts = roomLines.GetVerts();
            var wellVerts = wellLines.Select(p => p as Curve).ToList().GetVerts();
            spaceVerts.AddRange(wellVerts);

            //打散走廊路径线
            List<Curve> splitCorridorCurves = new List<Curve>();
            foreach (var corridorLine in corridorLines)
            {
                var corridorPts = spaceVerts.FindAll(p => corridorLine.IsOnLine(p));
                corridorPts.Add(corridorLine.EndPoint0());
                corridorPts.Add(corridorLine.EndPoint1());

                corridorPts = corridorPts.OrderBy(p => corridorLine.EndPoint0().DistanceTo(p)).ToList();
                for (int i = 0; i < corridorPts.Count - 1; i++)
                {
                    XYZ sPt = corridorPts[i];
                    XYZ ePt = corridorPts[i + 1];
                    if (sPt.DistanceTo(ePt).IsTolerance())
                        continue;
                    splitCorridorCurves.Add(Line.CreateBound(sPt, ePt));
                }
            }

            //走廊路径顶点
            var corridorVerts = splitCorridorCurves.GetVerts();
            //给走廊节点命名
            for (int i = 0; i < corridorVerts.Count; i++)
            {
                RouteNodeData routeNodeData = new RouteNodeData();
                routeNodeData.NodeName = "走廊节点" + (i + 1);                
                routeNodeData.NodeCoordinate = corridorVerts[i];
                routeNodeData.NodeType = NodeType.Corridor;

                var tuple = tupleCorridorElvs.Find(p => p.Item2.IsEquals(corridorVerts[i].Z));
                if (tuple != null)
                    routeNodeData.NodeLevel = tuple.Item1;

                routeNodes.Add(routeNodeData);
            }

            List<RouteNodeData> wellRouteNodeDatas = new List<RouteNodeData>();
            //管井节点命名，根据管井节点分组
            var dicWellNames = new Dictionary<string, List<XYZ>>();
            for (int i = 0; i < wellVerts.Count; i++)
            {
                XYZ vert = wellVerts[i];
                //与走廊交接节点，算走廊的
                if (corridorVerts.Exists(p => p.IsEqualInDistance(vert)))
                    continue;
                //管廊路径线
                var wellRoute = wellRoutes.Find(p => (p.GetLocationCurve() as Line).IsOnLine(vert));
                if (wellRoute == null)
                    continue;

                string wellName = wellRoute.GetParameterValueByName("标记");
                if (dicWellNames.ContainsKey(wellName))
                {
                    dicWellNames[wellName].Add(vert);
                }
                else
                {
                    dicWellNames.Add(wellName, new List<XYZ>() { vert });
                }
            }
            //根据位置命名
            foreach (var kvpWell in dicWellNames)
            {
                var wellRoute = wellRoutes.Find(p => p.GetParameterValueByName("标记") == kvpWell.Key);
                if (wellRoute == null)
                    continue;

                //与走廊连接的节点调整到第一个
                XYZ elvPt = kvpWell.Value.Find(p => tupleCorridorElvs.Exists(t => t.Item2.Equals(p.Z)));
                if (elvPt != null)
                {
                    kvpWell.Value.Remove(elvPt);
                    kvpWell.Value.Insert(0, elvPt);
                }

                for (int i = 0; i < kvpWell.Value.Count; i++)
                {
                    XYZ vert = kvpWell.Value[i];

                    RouteNodeData routeNodeData = new RouteNodeData();

                    if (i == 0)
                        routeNodeData.NodeName = kvpWell.Key;
                    else
                        routeNodeData.NodeName = kvpWell.Key + "|" + i.ToString();

                    routeNodeData.NodeCoordinate = vert;
                    routeNodeData.NodeType = NodeType.Well;

                    var wellLevel = levels.Find(p => p.ProjectElevation.IsEquals(wellRoute.ReferenceLevel.ProjectElevation));
                    if (wellLevel != null)
                        routeNodeData.NodeLevel = wellLevel;

                    wellRouteNodeDatas.Add(routeNodeData);
                }
            }            

            //遍历所有走廊节点，创建整个节点数据
            //房间节点在创建走廊相邻节点时创建
            List<RouteNodeData> roomRouteNodeDatas = new List<RouteNodeData>();
            foreach (var routeNodeData in routeNodes)
            {
                var nodePt = routeNodeData.NodeCoordinate;
                //相邻的走廊路径
                var neighborCurves = splitCorridorCurves.FindAll(p =>
                    p.EndPoint0().IsEqualInDistance(nodePt) || p.EndPoint1().IsEqualInDistance(nodePt));
                foreach (var neighborCurve in neighborCurves)
                {
                    XYZ neighborNodePt = neighborCurve.EndPoint0().IsEqualInDistance(nodePt)
                        ? neighborCurve.EndPoint1() : neighborCurve.EndPoint0();

                    var nodeData = routeNodes.Find(p => p.NodeCoordinate.IsEqualInDistance(neighborNodePt));
                    if (nodeData == null)
                        continue;
                    //添加相邻节点
                    NeighborNodeData neighborNodeData = new NeighborNodeData();
                    neighborNodeData.NodeName = nodeData.NodeName;
                    neighborNodeData.Distance = neighborCurve.Length;

                    routeNodeData.NeighborNodes.Add(neighborNodeData);
                }
                //相邻的房间路径
                var neighborRoomRounts = tupleRoomLines.FindAll(p =>
                     p.Item2.EndPoint0().IsEqualInDistance(nodePt) || p.Item2.EndPoint1().IsEqualInDistance(nodePt));
                foreach (var neighborRoomRount in neighborRoomRounts)
                {
                    var roomRountCurve = neighborRoomRount.Item2;
                    string roomName = neighborRoomRount.Item1;

                    NeighborNodeData neighborNodeData = new NeighborNodeData();
                    neighborNodeData.NodeName = roomName;
                    neighborNodeData.Distance = roomRountCurve.Length;

                    routeNodeData.NeighborNodes.Add(neighborNodeData);

                    //添加房间节点数据
                    XYZ neighborNodePt = roomRountCurve.EndPoint0().IsEqualInDistance(nodePt)
                        ? roomRountCurve.EndPoint1() : roomRountCurve.EndPoint0();

                    NeighborNodeData roomNeighborNodeData = new NeighborNodeData();
                    roomNeighborNodeData.NodeName = routeNodeData.NodeName;
                    roomNeighborNodeData.Distance = roomRountCurve.Length;

                    RouteNodeData roomRouteNodeData = new RouteNodeData();
                    roomRouteNodeData.NodeName = roomName;
                    roomRouteNodeData.NodeLevel = routeNodeData.NodeLevel;
                    roomRouteNodeData.NodeCoordinate = neighborNodePt;
                    roomRouteNodeData.NodeType = NodeType.Room;
                    roomRouteNodeData.NeighborNodes = new List<NeighborNodeData>() { roomNeighborNodeData };

                    roomRouteNodeDatas.Add(roomRouteNodeData);
                }

                //相邻的管井路径
                var neighborWellRounts = wellLines.FindAll(p =>
                     p.EndPoint0().IsEqualInDistance(nodePt) || p.EndPoint1().IsEqualInDistance(nodePt));
                foreach (var neighborWellRount in neighborWellRounts)
                {
                    //相邻的管井节点
                    XYZ neighborNodePt = neighborWellRount.EndPoint0().IsEqualInDistance(nodePt)
                        ? neighborWellRount.EndPoint1() : neighborWellRount.EndPoint0();
                    var nodeData = wellRouteNodeDatas.Find(p => p.NodeCoordinate.IsEqualInDistance(neighborNodePt));
                    if (nodeData == null)
                        continue;

                    //添加管井节点数据
                    NeighborNodeData neighborNodeData = new NeighborNodeData();
                    neighborNodeData.NodeName = nodeData.NodeName;
                    neighborNodeData.Distance = neighborWellRount.Length;

                    routeNodeData.NeighborNodes.Add(neighborNodeData);
                    //管井节点添加相邻节点
                    NeighborNodeData wellNeighborNodeData = new NeighborNodeData();
                    wellNeighborNodeData.NodeName = routeNodeData.NodeName;
                    wellNeighborNodeData.Distance = neighborWellRount.Length;

                    nodeData.NeighborNodes.Add(wellNeighborNodeData);
                }
            }

            //遍历管井节点，添加相邻的管井节点
            foreach (var routeNodeData in wellRouteNodeDatas)
            {
                var nodePt = routeNodeData.NodeCoordinate;
                //相邻的管井路径
                var neighborCurves = wellLines.FindAll(p =>
                    p.EndPoint0().IsEqualInDistance(nodePt) || p.EndPoint1().IsEqualInDistance(nodePt));
                foreach (var neighborCurve in neighborCurves)
                {
                    XYZ neighborNodePt = neighborCurve.EndPoint0().IsEqualInDistance(nodePt)
                        ? neighborCurve.EndPoint1() : neighborCurve.EndPoint0();

                    var nodeData = wellRouteNodeDatas.Find(p => p.NodeCoordinate.IsEqualInDistance(neighborNodePt));
                    if (nodeData == null)
                        continue;
                    //添加相邻节点
                    NeighborNodeData neighborNodeData = new NeighborNodeData();
                    neighborNodeData.NodeName = nodeData.NodeName;
                    neighborNodeData.Distance = neighborCurve.Length;

                    routeNodeData.NeighborNodes.Add(neighborNodeData);
                }
            }

            routeNodes.AddRange(wellRouteNodeDatas);
            routeNodes.AddRange(roomRouteNodeDatas);            

            return routeNodes;
        }

        /// <summary>
        /// 按路由创建水管
        /// </summary>
        /// <param name="routePaths"></param>
        internal static void CreatPipe(List<RouteNodeData> pathRouteNode, PipingSystemType pipeSystemType)
        {
            //建筑楼层标高
            var levels = Doc.OfClass<Level>().ToList()
                .FindAll(p => p.get_Parameter(BuiltInParameter.LEVEL_IS_BUILDING_STORY).AsInteger() == 1);        

            var routePipes = GetLinkRoutePipeByLevel(levels);     
            //房间路径线
            var roomRoutes = routePipes.FindAll(p =>  p.PipeType.Name.Contains("房间"));            

            //管道节点
            List<XYZ> listPipePt = new List<XYZ>();
            for (int i = 0; i < pathRouteNode.Count; i++)
            {
                RouteNodeData preNode = null;
                if (i != 0)
                    preNode = pathRouteNode[i - 1];

                RouteNodeData curNode = pathRouteNode[i];

                RouteNodeData nextNode = null;
                if(i != pathRouteNode.Count -1)
                    nextNode = pathRouteNode[i + 1];

                var roomRoute = roomRoutes.Find(p => p.GetParameterValueByName("标记") == curNode.NodeName);
                if (roomRoute != null)
                {
                    //房间标高与走廊不一致，增加管道拐点
                    double roomZ = roomRoute.GetLocationCurve().EndPoint0().Z;
                    if (preNode != null && !preNode.NodeCoordinate.Z.IsEquals(roomZ))
                        listPipePt.Add(preNode.NodeCoordinate.Flatten(roomZ));

                    listPipePt.Add(curNode.NodeCoordinate.Flatten(roomZ));

                    if (nextNode != null && !nextNode.NodeCoordinate.Z.IsEquals(roomZ))
                        listPipePt.Add(nextNode.NodeCoordinate.Flatten(roomZ));
                }
                else
                {
                    listPipePt.Add(curNode.NodeCoordinate);
                }
            }

            //管道线路
            List<Line> listPipeLine = new List<Line>();
            for (int i = 0; i < listPipePt.Count - 1; i++)
            {
                XYZ sPipePt = listPipePt[i];
                XYZ ePipePt = listPipePt[i + 1]; 
                listPipeLine.Add(Line.CreateBound(sPipePt, ePipePt));
            }

            listPipeLine = listPipeLine.JoinLines();

            var pipeTypes = Doc.OfClass<PipeType>().ToList();           

            //创建管道
            Pipe prePipe = null;
            for (int i = 0; i < listPipeLine.Count; i++)
            {
                Line pipeLine = listPipeLine[i];
                Pipe pipe = Pipe.Create(Doc, pipeSystemType.Id, pipeTypes.FirstOrDefault().Id, Doc.ActiveView.GenLevel.Id,
                    pipeLine.EndPoint0(), pipeLine.EndPoint1());
                pipe.SetDiameter(50d.MMToFeet());
                if (prePipe != null)
                {
                    if (prePipe.GetConnectorsByMinDistance(pipe, out var connector1, out var connector2))
                        Doc.Create.NewElbowFitting(connector1, connector2);
                }
                prePipe = pipe;
            }
        }




        /// <summary>
        /// 根据标高获取链接模型的房间
        /// </summary>
        /// <returns></returns>
        internal static List<Room> GetLinkRoom()
        {
            List<Room> linkRooms =  new List<Room>();
            //获取链接模型的房间
            var linkInstances = Doc.OfClass<RevitLinkInstance>().Where(o => o.HasLinked());
            foreach (var item in linkInstances)
            {
                var linkdoc = item.GetLinkDocument();
                var itemRooms = linkdoc.OfClass<SpatialElement>(BuiltInCategory.OST_Rooms).Cast<Room>().ToList();
                if (itemRooms.Any())
                {
                    linkRooms.AddRange(itemRooms);
                }
            }

            return linkRooms;
        }

        /// <summary>
        /// 根据标高获取链接模型可使用的路径线
        /// </summary>
        /// <returns></returns>
        internal static List<Pipe> GetLinkRoutePipeByLevel(List<Level> levels)
        {
            List<Pipe> linkRoutePipes =  new List<Pipe>();
            //获取链接模型的房间
            var linkInstances = Doc.OfClass<RevitLinkInstance>().Where(o => o.HasLinked());
            foreach (var item in linkInstances)
            {
                var linkdoc = item.GetLinkDocument();
                var itemRoutes = linkdoc.OfClass<MEPCurve>(BuiltInCategory.OST_PlaceHolderPipes).Cast<Pipe>()
                    .Where(o => levels.Exists(l => o.ReferenceLevel.ProjectElevation.IsEquals(l.ProjectElevation))).ToList();
                if (itemRoutes.Any())
                {
                    List<Pipe> routes = new List<Pipe>();
                    //房间路径线取水平路径线
                    routes.AddRange(itemRoutes.FindAll(p => p.PipeType.Name.Contains("房间") && 
                        p.GetLocationCurve() is Line line && line.Direction.Z.IsEquals(0)));
                    //其他路径线
                    routes.AddRange(itemRoutes.FindAll(p => p.PipeType.Name.Contains("走廊") ||
                        p.PipeType.Name.Contains("管井")));

                    linkRoutePipes.AddRange(routes);
                }
            }

            return linkRoutePipes;
        }
    }
}
